﻿using UnityEngine;

using Game.Player;

/// <summary>
/// 可与player产生互动类，用于会休眠、可在距离内互动的类：
/// <para>--如需Start、Update，需显式调用Start/Update方法；</para>
/// <para>对于WakeUp和Sleep动作预先发生于进入Player视野时</para>
/// <para>EnterRange和ExitRange事件则发生于Player更近一步时</para>
/// </summary>
/// <remarks>对应的动作也应该予以重写，否则此类无意义</remarks>
public abstract class InteractiveGameObj:MonoBehaviour  
{
    
    private enum ObjStatus 
    {
        /// <summary>
        /// 睡眠状态，只能进入唤醒状态
        /// </summary>
        Sleep,

        /// <summary>
        /// 被唤醒状态，能进入睡眠，或者范围内状态
        /// </summary>
        Awake,

        /// <summary>
        /// 范围内状态，只能进入唤醒状态
        /// </summary>
        InRange,
    }

    /// <summary>
    /// 当前Obj的状态
    /// </summary>
    [SerializeField]ObjStatus m_objStatus = ObjStatus.Awake;

    /// <summary>
    /// 检测是否休眠的时间间隔
    /// </summary>
    float m_checkDisableTimeDelta = 3F;


    /// <summary>
    /// 用于检测是否关闭的距离
    /// </summary>
    [Tooltip("用于检测是否关闭物体活动的距离")]
    [SerializeField] float m_checkDisableRange = 50;

    /// <summary>
    /// 判定进入互动，如开始攻击的距离
    /// </summary>
    [Tooltip("判定进入互动，如开始攻击的距离")]
    [SerializeField] float m_checkInteractRange = 10;

    /// <summary>
    /// 检测互动距离的时间间隔
    /// </summary>
    [SerializeField]float m_checkInteractTimeDelta = 1f;



    /// <summary>
    /// Transform 缓存
    /// </summary>
    protected Transform m_transform;

    /// <summary>
    /// Player 缓存
    /// </summary>
    protected Transform m_playerTrans;

    protected virtual void Start( )
    {
        m_transform = this.transform;
        m_playerTrans = PlayerDelegate.playerTransform.transform;

        SetInteractRangeDistance(m_checkInteractRange);
        SetSleepDistance(m_checkDisableRange);
    }
    
    //--检查时间
    float disCheckTime = 0;
    float interactCheckTime = 0;

    /// <summary>
    /// 互动范围m_checkInteractRange的平方数
    /// </summary>
    float m_interactRangeSqrDistance;

    /// <summary>
    /// 最大休眠距离m_checkInteractRange的2次方
    /// </summary>
    float m_sleepSqrDistance = 50 * 50;

    /// <summary>
    /// 执行休眠/唤醒操作
    /// （对应的操作重写在DoXXXXAction中）
    /// </summary>
	protected virtual void Update () 
    {
        //非范围内时，则检查是否要关闭物体
        if (!IsInRangeStatus()) //等同于 if (IsStatus(ObjStatus.Awake) || IsStatus(ObjStatus.Sleep))
        {
            disCheckTime -= Time.deltaTime;

            if (disCheckTime <= 0)
            {
                #region 该查关闭距离了：
                disCheckTime = m_checkDisableTimeDelta;

                //--到玩家的距离如果超过预定距离，则睡眠：
                if (GetSqrDistanceToPlayer() > m_sleepSqrDistance)
                {
                    if (IsInAwakeStatus())
                    {
                        m_objStatus = ObjStatus.Sleep;
                        OnSleep();
                    }
                }
                else if (IsInSleepStatus())
                {
                    m_objStatus = ObjStatus.Awake;
                    OnWakeUp();
                }
                #endregion
            }
        }

        //非睡眠状态下，则检查是否在范围内
        if(!IsInSleepStatus() )     //等同于 if (IsStatus(ObjStatus.Awake)|| IsStatus(ObjStatus.InRange))
        {
            interactCheckTime -= Time.deltaTime;
            if (interactCheckTime <= 0)
            {
                #region 该查是否互动了：
                interactCheckTime = m_checkInteractTimeDelta;

                //--玩家到距离内了，则互动：
                if (GetSqrDistanceToPlayer() < m_interactRangeSqrDistance)
                {
                    if (IsInAwakeStatus()
                        && DoPlayerEnterRange())
                    {
                        m_objStatus = ObjStatus.InRange;
                        
                    }
                }
                else if (IsInRangeStatus()) //--否则即距离超出，则尝试出范围：
                {
                    if (DoPlayerExitRange())
                    {
                        m_objStatus = ObjStatus.Awake;
                    }
                }
                #endregion
            }
        }
    }

    #region 提供给子类访问的函数区域
    /// <summary>
    /// 是否是睡眠状态
    /// <para>可用于怪物不再更新自身逻辑</para>
    /// </summary>
    protected bool IsInSleepStatus()
    {
        return m_objStatus == ObjStatus.Sleep;
    }

    /// <summary>
    /// 是否是唤醒状态（但未入范围内）
    /// <para>可用于让怪物开始四周走动</para>
    /// </summary>
    protected bool IsInAwakeStatus()
    {
        return m_objStatus == ObjStatus.Awake;
    }

    /// <summary>
    /// 是否是在范围内状态
    /// <para>可用于怪物等是否发现玩家</para>
    /// </summary>
    protected bool IsInRangeStatus()
    {
        return m_objStatus == ObjStatus.InRange;
    }

    /// <summary>
    /// 获得到玩家的平方距离
    /// </summary>
    protected float GetSqrDistanceToPlayer()
    {
        return m_playerTrans.SqrDistanceTo(m_transform.position);
    }

    /// <summary>
    /// 设置：每轮检查休眠与否的时间间隔
    /// </summary>
    protected void SetCheckSleepTimeDelta(float checkSleepTimeDelta)
    {
        m_checkDisableTimeDelta = checkSleepTimeDelta;
    }

    /// <summary>
    /// 设置：每轮检查进入范围与否的时间间隔
    /// </summary>
    protected void SetCheckRangeTimeDelta(float checkRangeTime)
    {
        m_checkInteractTimeDelta = checkRangeTime;
    }

    /// <summary>
    /// 设置：关闭距离，如果与玩家超粗此距离，则休眠物体
    /// </summary>
    /// <param name="sleepDistance">休眠物体的距离</param>
    protected void SetSleepDistance(float sleepDistance)
    {
        m_sleepSqrDistance =  sleepDistance * sleepDistance;    //为了加速计算，用三次方
    }

    /// <summary>
    /// <para>设置：判定进入互动圈的范围</para>
    /// <para>如 敌人发现玩家进入此互动圈范围，则追击玩家</para>
    /// </summary>
    protected void SetInteractRangeDistance(float interactDistance)
    {
        m_interactRangeSqrDistance = interactDistance * interactDistance;
    }

    #endregion

    #region 子类可重写部分
    /// <summary>
    /// 休眠物体所执行的动作。空函数，可不复写
    /// <para>--发生于玩家与物体远超出范围，睡眠时一次调用</para>
    /// </summary>
    protected virtual void OnSleep()
    {
        //Debug.LogWarning("obj sleep:");
    }

    /// <summary>
    /// 唤醒物体所执行的操作，空函数。可不复写
    /// <para>--发生于进入玩家视野Visible，唤醒时一次调用</para>
    /// </summary>
    protected virtual void OnWakeUp()
    {
        //Debug.LogWarning("obj wake up:");
    }

    /// <summary>
    /// 当玩家进入范围内的动作，并返回此物体是否转变状态
    /// </summary>
    /// <returns>如果返回true，则表示应该转变成进入range状态；否则则无法进入状态</returns>
    protected virtual bool DoPlayerEnterRange()
    {
        return true;
        //Debug.LogWarning("Player Enter range");
    }

    /// <summary>
    /// 当玩家退出范围内的动作，需要复写是否可以转变状态
    /// <remarks>非特殊情况，无需改写返回值，直接返回true即可</remarks>
    /// </summary>
    /// <returns>如果返回true，则表示应该转变成退出range状态；否则则无法退出状态</returns>
    protected virtual bool DoPlayerExitRange()
    {
        return true;
        //Debug.LogWarning("Player Exit range");
    }

    #endregion

    //--绘图
#if UNITY_EDITOR
    [SerializeField] bool showRangeGizmos = false;

    //绘制检测范围
    void OnDrawGizmosSelected()
    {
        if (showRangeGizmos )
        {
            //显示出可绘制的区域
            Vector3 center = this.transform.position;
            Gizmos.color = Color.green;
            Gizmos.DrawWireSphere(center, m_checkDisableRange);
        }
    }

#endif


}
